﻿using System;
using System.Diagnostics;
// using System.Runtime.InteropServices;

namespace Mindless
{
    /// <summary>
    /// Piece colors.
    /// </summary>
    enum Color : byte { None = 0, Black = 1, White = 2 };

    /// <summary>
    /// Piece types. A non-king piece is called a "pawn".
    /// </summary>
    enum PieceType : sbyte { None = 0, Pawn = 1, King = 2 };

    /// <summary>
    /// A single checkers piece on a board. Highly optimized to reduce memory usage.
    /// </summary>
    struct Piece : IEquatable<Piece>
    {
        /// <summary>
        /// Constructs a new piece.
        /// </summary>
        public Piece(Color color, PieceType type)
        {
            this.value = 0;
            this.color = color;
            this.type = type;
        }

        /// <summary>
        /// The receiver's color.
        /// </summary>
        public Color Color
        {
            get { return this.color; }
        }
        Color color
        {
            set { this.value = (byte) ((this.value & 0x0f) | ((byte) value << 4)); }
            get { return (Color) ((this.value & 0xf0) >> 4);  }
        }

        /// <summary>
        /// The receiver's type.
        /// </summary>
        public PieceType Type
        {
            get { return this.type; }
        }
        PieceType type
        {
            set { this.value = (byte) ((this.value & 0xf0) | (byte) value); }
            get { return (PieceType) (this.value & 0x0f); }
        }
        byte value;

        /// <summary>
        /// Each empty square contains the null piece.
        /// </summary>
        public static Piece Null      = new Piece(Color.None, PieceType.None);
        public static Piece BlackPawn = new Piece(Color.Black, PieceType.Pawn);
        public static Piece BlackKing = new Piece(Color.Black, PieceType.King);
        public static Piece WhitePawn = new Piece(Color.White, PieceType.Pawn);
        public static Piece WhiteKing = new Piece(Color.White, PieceType.King);

        /// <summary>
        /// Indicates whether two pieces are equal.
        /// </summary>
        public bool Equals(Piece piece)
        {
            return (piece.color == this.color) && (piece.type == this.type);
        }

        /// <summary>
        /// Indicates whether two pieces are equal.
        /// </summary>
        public override bool Equals(object obj)
        {
            return base.Equals((Piece) obj);
        }

        /// <summary>
        /// Indicates whether two pieces are equal.
        /// </summary>
        public static bool operator ==(Piece a, Piece b)
        {
            return a.Equals(b);
        }

        /// <summary>
        /// Indicates whether two pieces are not equal.
        /// </summary>
        public static bool operator !=(Piece a, Piece b)
        {
            return !a.Equals(b);
        }

        /// <summary>
        /// Receiver's hash value.
        /// </summary>
        public override int GetHashCode()
        {
            return (int) this.color + (int) this.type;
        }

        /// <summary>
        /// Display string.
        /// </summary>
        public override string ToString()
        {
            return string.Format("{0} {1}", this.color, this.type);
        }

        /// <summary>
        /// Black -> White, White -> Black.
        /// </summary>
        public static Color OppositeColor(Color color)
        {
            Debug.Assert(color != Color.None);

            return (Color) (3 - (int) color);
        }

        /// <summary>
        /// The direction in which the given color travels on the board.
        /// </summary>
        public static int YDirection(Color color)
        {
            Debug.Assert(color != Color.None);

            int result = 0;
            switch (color)
            {
                case Color.Black: result = -1; break;   // black moves down
                case Color.White: result = 1; break;    // white moves up
            }

            return result;
        }
    }
}
